#ifdef _WINDOWS
#include <windows.h>
#endif

#include <gl/Gl.h>
#include <gl/Glu.h>
#include <math.h>
#include "OGLWindow.h"

const float worldCloseDistance = 1.0f;
const float worldFarDistance = 1000.0f;
const float snipeView = 12.0f;
const float regularView = 52.0f;

// disable implicit float-double casting
#pragma warning(disable:4305)

OGLWindow::OGLWindow()
{
	fill( keys, keys + sizeof(keys), false );
	viewAngle = regularView;
	
}

OGLWindow::~OGLWindow()
{
}

bool OGLWindow::Init()
{
    // clear to black background
    glClearColor(0.0, 0.0, 0.0, 0.0);
	mouseSensitivity = 2.0f;
    m_angle = 0.0f;
	gameCamera = new CCamera;

	gameWorld = new CWorld(gameCamera);
	gameWorld->setWorldDistance(worldCloseDistance, worldFarDistance);
    return true;
}

bool OGLWindow::Shutdown()
{
	delete gameWorld;
	delete gameCamera;
	gameWorld = NULL;
	gameCamera = NULL;


    return true;
}

void OGLWindow::SetupProjection(int width, int height)
{
    if (height == 0) // prevent dividing by zero
        height = 1;               

    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);    
    glLoadIdentity();

    // calculate aspect ratio of window
    gluPerspective(viewAngle,(GLfloat)width/(GLfloat)height,1.0f,1000.0f);
	

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    m_windowWidth = width;
    m_windowHeight = height;
}

void OGLWindow::Prepare() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     
	glLoadIdentity();
    m_angle += 0.1f;
}

int OGLWindow::GetMouseX(LPARAM lParam)
{
	return LOWORD(lParam);
}

int OGLWindow::GetMouseY(LPARAM lParam)
{
	return HIWORD(lParam);
}


void OGLWindow::OnMouseMove(int x, int y) {
	if (!gameWorld->IsGameDone()) {
		gameCamera->yaw += 0.25f * mouseSensitivity * (x - this->m_windowWidth/2);
		gameCamera->pitch += 0.25f * mouseSensitivity * (this->m_windowHeight/2 - (float)y);
	}
}

void OGLWindow::OnKeyUp(int nVirtKey) {
	if ( 0 <= nVirtKey && nVirtKey < 256 )
		keys[nVirtKey] = false;
}

void OGLWindow::OnKeyDown(int nVirtKey)
{
	if (!gameWorld->IsGameDone())
	{
		if ( 0 <= nVirtKey && nVirtKey < 256 )
			keys[nVirtKey] = true;

		switch (nVirtKey)
		{
		case VK_ADD:
			mouseSensitivity += 0.05f;
			break;
		case VK_SUBTRACT:
			mouseSensitivity -= 0.05f;
			if (mouseSensitivity < 0.05)
				mouseSensitivity = 0.05;
			break;
		case '1'://rocket stuff
			
			viewAngle = regularView;
			SetupProjection(m_windowWidth,m_windowHeight);
			gameWorld->player->changeGun(1);
			break;
		case '2'://bfg9000
			
			viewAngle = regularView;
			SetupProjection(m_windowWidth,m_windowHeight);
			gameWorld->player->changeGun(2);
			break;
		case '3'://snipe stuff
			
			viewAngle = snipeView;
			SetupProjection(m_windowWidth,m_windowHeight);
			gameWorld->player->changeGun(3);
			break;
		default:
			break;
		}
	}
}

void OGLWindow::OnMouseDownL(int x, int y) 
{
	if (!gameWorld->IsGameDone())
	{
		gameWorld->player->FireWeapon();
	}
}

void OGLWindow::Render(float deltaTime)
{
	CCamera *camera = OnGetCamera();
	CWorld *world = OnGetWorld();

	
	static const float speed = 70;
	float drive = 0, strafe = 0, jump = 0;
	if ( keys[VK_UP] || keys['W'] )		drive ++;
	if ( keys[VK_DOWN] || keys['S'] )	drive --;
	if ( keys[VK_LEFT] || keys['A'] )	strafe --;
	if ( keys[VK_RIGHT] || keys['D'] )	strafe ++;
	if ( keys[' '] ) jump++;

	if ( drive || strafe || jump ) {
		if ( GetKeyState(VK_LSHIFT)&0x8000 ) { drive *= 2.5; strafe *= 2.5; }
		gameCamera->velocity += CVector(speed*strafe,200*speed*jump,speed*drive) * deltaTime;
	}
	gameCamera->acceleration.y = -10; // -0.0001 * deltaTime;

	world->Prepare();	
	camera->Animate(deltaTime);
	world->Animate(deltaTime);
	world->Draw(camera);
}

int OGLWindow::getWidth() const {
	return m_windowWidth;
}

int OGLWindow::getHeight() const {
	return m_windowHeight;
}

